﻿using System;
using System.Collections.Generic;
using System.Linq;
using data.contracts;

namespace modelbuilder.parts
{
    public class ExpandJoinWires : IExpandJoinWires
    {
        private const char PinSeparator = ';';
        public event Action<IEnumerable<Board>> Out_Result;

        public void In_Process(IEnumerable<Board> boards) {
            Out_Result(ExpandAllJoinWires(boards));
        }

        private static IEnumerable<Board> ExpandAllJoinWires(IEnumerable<Board> boards) {
            return boards.Select(ExpandAllJoinWires);
        }

        private static Board ExpandAllJoinWires(Board board) {
            var wires = ExpandAllJoinWires(board.Wires.ToArray());
            board.ReplaceWires(wires);
            return board;
        }

        private static IEnumerable<Wire> ExpandAllJoinWires(IEnumerable<Wire> wires) {
            var number = 1;
            foreach (var wire in wires) {
                if (wire.From.Contains(PinSeparator)) {
                    foreach (var newWire in ExpandJoinWire(wire, number++)) {
                        yield return newWire;
                    }
                }
                else {
                    yield return wire;
                }
            }
        }

        private static IEnumerable<Wire> ExpandJoinWire(Wire wire, int number) {
            var types = ExtractTypeInformation(wire.Type);
            var joinType = string.Format("{0},{1},{2}", types.Item1, types.Item2, types.Item3);
            var joinName = string.Format("$join{0}", number);
            var joiner = string.Format("(IJoin<{0}>) {1}", joinType, joinName);
            var inputPins = wire.From.Split(PinSeparator);

            yield return new Wire(inputPins[0].Trim(), types.Item1, joiner + ".Input1");
            yield return new Wire(inputPins[1].Trim(), types.Item2, joiner + ".Input2");
            yield return new Wire(joiner + ".Output", types.Item3, wire.To);
        }

        private static Tuple<string,string,string> ExtractTypeInformation(string type) {
            var types = type.Split(':');
            if (types.Length == 1) {
                return new Tuple<string, string, string>(type, type, type);
            }
            // type="(string; string) : Tuple{string, string}"
            var inputTypes = types[0].Trim().TrimStart('(').TrimEnd(')').Split(PinSeparator);
            var outputType = types[1].Trim();
            return new Tuple<string, string, string>(inputTypes[0].Trim(), inputTypes[1].Trim(), outputType);
        }
    }
}